#include <Editor/StdAfx.h>
#include <Editor/CandidatesDataProvider.h>
#include <fstream>
#include <wx/tokenzr.h>
#include <wx/textfile.h>

unsigned count_starting_character(const wxString& str, wxChar c)
{
	wxString::const_iterator it = str.begin(), itEnd = str.end();
	unsigned i=0;
	for ( ; it!=itEnd; it++, i++)
	{
		if (*it != c)
			return i;
	}
	
	return 0;
}

CandidatesDataProvider::CandidatesDataProvider(const wxString& dataFilepath)
{
	mRoot.mKeystring = _T("ROOT");
	mCurrentParentCandidate = &mRoot;
	if (!wxFileExists(dataFilepath))
		return;

	unsigned currentTabCount = 0;
	CandidateNode* lastAddedCandidate = 0;
	std::stack<CandidateNode*> stack;

	wxTextFile file(dataFilepath);
	file.Open(dataFilepath);	
	unsigned lineCount = file.GetLineCount();
	mCandidates.reserve(lineCount);
	
	wxString strCandidate;
	for (strCandidate = file.GetFirstLine(); !file.Eof(); strCandidate = file.GetNextLine())
	{
		unsigned tabCount = count_starting_character(strCandidate, '\t');
		strCandidate = strCandidate.Trim(true).Trim(false);
		if (strCandidate.empty())
			continue;

		if (tabCount == currentTabCount)
		{
			/*No additional work*/	
		}
		else if (tabCount > currentTabCount)
		{
			assert(tabCount - currentTabCount == 1);
			currentTabCount = tabCount;
			stack.push(mCurrentParentCandidate);
			mCurrentParentCandidate = lastAddedCandidate;
		}
		else // (tabCount < currentTabCount)
		{
			while (tabCount!=currentTabCount)
			{
				currentTabCount--;
				mCurrentParentCandidate = stack.top();
				stack.pop();
			}
		}

		mCandidates.push_back(CandidateNode());
		CandidateNode& addedCandidate = mCandidates.back();
		unsigned found_at = strCandidate.Find(_T("@"));
		if (found_at != wxString::npos)
		{
			wxString keyString = strCandidate.substr(0, found_at);
			keyString = keyString.Trim(false).Trim(true);
			addedCandidate.mKeystring = keyString;
			addedCandidate.mDesc = strCandidate.substr(found_at+1).c_str();
		}
		else
		{
			addedCandidate.mKeystring = strCandidate;
		}	

		addedCandidate.mParent = mCurrentParentCandidate;
		mCurrentParentCandidate->mChildren.push_back(&addedCandidate);
		lastAddedCandidate = &addedCandidate;
	}
	mCurrentParentCandidate = &mRoot;

}

CandidatesDataProvider::~CandidatesDataProvider()
{

}

void CandidatesDataProvider::Update(const wxString& editingScript)
{
	int size = (int)editingScript.Len();
	int countBrace = 0;
	unsigned found = wxString::npos;
	for (int i=size-1; i>=0; i--)
	{
		if (editingScript[i] == '{')
		{
			countBrace++;
			if (countBrace == 1)
			{
				found = i;
				break;
			}
		}
		else if (editingScript[i] == '}')
		{
			countBrace--;
		}
	}
	if (found != wxString::npos)
	{
		unsigned end = editingScript.find_last_not_of(_T(" \t\n"), found-1) + 1;
		unsigned start = editingScript.find_last_of(_T('\n'), end-1) + 1;
		wxString keyword = editingScript.substr(start, end-start);
		unsigned size = mCandidates.size();
		wxString strKeyword = keyword.c_str();
		strKeyword = strKeyword.Trim(true).Trim(false);
		wxArrayString tokens = wxStringTokenize(strKeyword);
		for (unsigned i=0; i<size; i++)
		{
			if ( tokens[0] == mCandidates[i].mKeystring || 
				strKeyword == mCandidates[i].mKeystring)
			{
				mCurrentParentCandidate = &mCandidates[i];

				found = editingScript.find_last_of('\n') + 1;
				wxString strTyping = editingScript.substr(found).c_str();
				strTyping = strTyping.Trim(true).Trim(false);
				mCurrentCandidates.clear();
				unsigned childrenSize = mCurrentParentCandidate->mChildren.size();
				for (unsigned c = 0; c<childrenSize; c++)
				{
					if (
						strTyping.empty() || 
						mCurrentParentCandidate->mChildren[c]->mKeystring.find_first_of(strTyping) == 0
						)
					{
						mCurrentCandidates.push_back(mCurrentParentCandidate->mChildren[c]);
					}
				}
				return;
			}
		}
		mCurrentParentCandidate = 0;
	}
	else
	{
		mCurrentCandidates.clear();
		mCurrentCandidates.push_back(mRoot.mChildren[0]);
		mCurrentParentCandidate = &mRoot;
	}
}